home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / nevow / guard.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-03-23  |  22KB  |  556 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''
  5. Resource protection for Nevow. If you wish to use twisted.cred to protect your
  6. Nevow application, you are probably most interested in
  7. L{SessionWrapper}.
  8. '''
  9. __metaclass__ = type
  10. import random
  11. import time
  12. import md5
  13. import StringIO
  14. from zope.interface import implements
  15. from twisted.python import log, components
  16. from twisted.internet import defer
  17. from twisted.cred.error import UnauthorizedLogin
  18. from twisted.cred.credentials import UsernamePassword, Anonymous
  19.  
  20. try:
  21.     from twisted.web import http
  22. except ImportError:
  23.     from twisted.protocols import http
  24.  
  25. from nevow import inevow, url, stan
  26.  
  27. def _sessionCookie():
  28.     return md5.new('%s_%s' % (str(random.random()), str(time.time()))).hexdigest()
  29.  
  30.  
  31. class GuardSession(components.Componentized):
  32.     """A user's session with a system.
  33.  
  34.     This utility class contains no functionality, but is used to
  35.     represent a session.
  36.     """
  37.     implements(inevow.ISession, inevow.IGuardSession)
  38.     
  39.     def __init__(self, guard, uid):
  40.         '''Initialize a session with a unique ID for that session.
  41.         '''
  42.         components.Componentized.__init__(self)
  43.         self.guard = guard
  44.         self.uid = uid
  45.         self.expireCallbacks = []
  46.         self.checkExpiredID = None
  47.         self.setLifetime(60)
  48.         self.portals = { }
  49.         self.touch()
  50.  
  51.     
  52.     def getLoggedInRoot(self):
  53.         '''Get the most-recently-logged-in avatar.
  54.         '''
  55.         if len(self.portals) != 1:
  56.             raise RuntimeError('Ambiguous request for current avatar.')
  57.         len(self.portals) != 1
  58.         return self.portals.values()[0][0]
  59.  
  60.     
  61.     def resourceForPortal(self, port):
  62.         return self.portals.get(port)
  63.  
  64.     
  65.     def setDefaultResource(self, rsrc, logout):
  66.         '''
  67.  
  68.         Change the root-resource available to the user who has already
  69.         authenticated anonymously.  This only works in applications that DO NOT
  70.         use the multiple-simultaneous-portals feature.  If you do not know what
  71.         this means, you may safely ignore it.
  72.  
  73.         '''
  74.         if len(self.portals) != 1:
  75.             raise RuntimeError('Ambiguous request for current avatar.')
  76.         len(self.portals) != 1
  77.         self.setResourceForPortal(rsrc, self.portals.keys()[0], logout)
  78.  
  79.     
  80.     def setResourceForPortal(self, rsrc, port, logout):
  81.         '''Change the root-resource available to a user authenticating against a given
  82.         portal.
  83.  
  84.         If a user was already logged in to this session from that portal, first
  85.         log them out.
  86.  
  87.         @param rsrc: an L{IResource} implementor.
  88.         @param port: a cred Portal instance.
  89.         @param logout: a 0-arg callable to be invoked upon logout.
  90.         '''
  91.         self.portalLogout(port)
  92.         self.portals[port] = (rsrc, logout)
  93.         return rsrc
  94.  
  95.     
  96.     def portalLogout(self, port):
  97.         '''
  98.         If we have previously acccepted a login for this portal, call its
  99.         logout method and de-associate that portal from this session, catching
  100.         any errors from the logout method.
  101.  
  102.         Otherwise: do nothing.
  103.  
  104.         @param port: a cred Portal.
  105.         '''
  106.         p = self.portals.get(port)
  107.         if p:
  108.             log.msg('Logout of portal %r' % port)
  109.             (r, l) = p
  110.             
  111.             try:
  112.                 l()
  113.             except:
  114.                 log.err()
  115.  
  116.             del self.portals[port]
  117.         
  118.  
  119.     
  120.     def setLifetime(self, lifetime):
  121.         """Set the approximate lifetime of this session, in seconds.
  122.  
  123.         This is highly imprecise, but it allows you to set some general
  124.         parameters about when this session will expire.  A callback will be
  125.         scheduled each 'lifetime' seconds, and if I have not been 'touch()'ed
  126.         in half a lifetime, I will be immediately expired.
  127.         """
  128.         self.lifetime = lifetime
  129.  
  130.     
  131.     def notifyOnExpire(self, callback):
  132.         '''Call this callback when the session expires or logs out.
  133.         '''
  134.         self.expireCallbacks.append(callback)
  135.  
  136.     
  137.     def expire(self):
  138.         '''Expire/logout of the session.
  139.         '''
  140.         log.msg('expired session %s' % str(self.uid))
  141.         del self.guard.sessions[self.uid]
  142.         for portal in self.portals.keys():
  143.             self.portalLogout(portal)
  144.         
  145.         for c in self.expireCallbacks:
  146.             
  147.             try:
  148.                 c()
  149.             continue
  150.             log.err()
  151.             continue
  152.  
  153.         
  154.         self.expireCallbacks = []
  155.         if self.checkExpiredID:
  156.             self.checkExpiredID.cancel()
  157.             self.checkExpiredID = None
  158.         
  159.  
  160.     
  161.     def touch(self):
  162.         self.lastModified = time.time()
  163.  
  164.     
  165.     def checkExpired(self):
  166.         reactor = reactor
  167.         import twisted.internet
  168.         self.checkExpiredID = None
  169.         if time.time() - self.lastModified > self.lifetime / 2:
  170.             if self.guard.sessions.has_key(self.uid):
  171.                 self.expire()
  172.             else:
  173.                 log.msg('no session to expire: %s' % str(self.uid))
  174.         else:
  175.             log.msg('session given the will to live for %s more seconds' % self.lifetime)
  176.             self.checkExpiredID = reactor.callLater(self.lifetime, self.checkExpired)
  177.  
  178.     
  179.     def __getstate__(self):
  180.         d = self.__dict__.copy()
  181.         if d.has_key('checkExpiredID'):
  182.             del d['checkExpiredID']
  183.         
  184.         return d
  185.  
  186.     
  187.     def __setstate__(self, d):
  188.         self.__dict__.update(d)
  189.         self.touch()
  190.         self.checkExpired()
  191.  
  192.  
  193.  
  194. def urlToChild(ctx, *ar, **kw):
  195.     u = url.URL.fromContext(ctx)
  196.     for segment in ar:
  197.         u = u.child(stan.xml(segment))
  198.     
  199.     if inevow.IRequest(ctx).method == 'POST':
  200.         u = u.clear()
  201.     
  202.     for k, v in kw.items():
  203.         u = u.replace(k, v)
  204.     
  205.     return u
  206.  
  207. SESSION_KEY = '__session_key__'
  208. LOGIN_AVATAR = '__login__'
  209. LOGOUT_AVATAR = '__logout__'
  210.  
  211. def nomind(*args):
  212.     pass
  213.  
  214.  
  215. class Forbidden(object):
  216.     implements(inevow.IResource)
  217.     
  218.     def locateChild(self, ctx, segments):
  219.         return self
  220.  
  221.     
  222.     def renderHTTP(self, ctx):
  223.         request = inevow.IRequest(ctx)
  224.         request.setResponseCode(http.FORBIDDEN)
  225.         return '<html><head><title>Forbidden</title></head><body><h1>Forbidden</h1>Request was forbidden.</body></html>'
  226.  
  227.  
  228.  
  229. class SessionWrapper:
  230.     '''
  231.     SessionWrapper
  232.  
  233.     The following class attributes can be modified on an instance
  234.     of the class.
  235.  
  236.     @ivar secureCookies: Whether to use secure (TLS only) cookies or not.
  237.       True (default): make cookies secure when session is initiated
  238.       in a secure (TLS) connection.
  239.  
  240.       False: cookies do not get the secure attribute.
  241.  
  242.     @ivar: persistentCookies: Whether to use persistent (saved to disk) cookies or not.
  243.       True: make cookies persistent, so they are valid for the
  244.         length of the sessionLifetime even if the browser window
  245.         is closed.
  246.  
  247.       False (default): cookies do not get saved to disk, and thus last
  248.         only as long as the session does.  If the browser is
  249.         closed before the session timeout, both the session
  250.         and the cookie go away.
  251.     '''
  252.     implements(inevow.IResource)
  253.     sessionLifetime = 3600
  254.     sessionFactory = GuardSession
  255.     credInterface = inevow.IResource
  256.     useCookies = True
  257.     secureCookies = True
  258.     persistentCookies = False
  259.     
  260.     def __init__(self, portal, cookieKey = None, mindFactory = None, credInterface = None, useCookies = None):
  261.         self.portal = portal
  262.         if cookieKey is None:
  263.             cookieKey = 'woven_session_' + _sessionCookie()
  264.         
  265.         self.cookieKey = cookieKey
  266.         self.sessions = { }
  267.         if mindFactory is None:
  268.             mindFactory = nomind
  269.         
  270.         self.mindFactory = mindFactory
  271.         if credInterface is not None:
  272.             self.credInterface = credInterface
  273.         
  274.         if useCookies is not None:
  275.             self.useCookies = useCookies
  276.         
  277.         self.resource = self
  278.  
  279.     
  280.     def renderHTTP(self, ctx):
  281.         request = inevow.IRequest(ctx)
  282.         d = defer.maybeDeferred(self._delegate, ctx, [])
  283.         
  284.         def _cb(.0, ctx):
  285.             (resource, segments) = .0
  286.             if not not segments:
  287.                 raise AssertionError
  288.             res = inevow.IResource(resource)
  289.             return res.renderHTTP(ctx)
  290.  
  291.         d.addCallback(_cb, ctx)
  292.         return d
  293.  
  294.     
  295.     def locateChild(self, ctx, segments):
  296.         request = inevow.IRequest(ctx)
  297.         path = segments[0]
  298.         if self.useCookies:
  299.             cookie = request.getCookie(self.cookieKey)
  300.         else:
  301.             cookie = ''
  302.         if path.startswith(SESSION_KEY):
  303.             key = path[len(SESSION_KEY):]
  304.             if key not in self.sessions:
  305.                 return (urlToChild(ctx, *segments[1:], **{
  306.                     '__start_session__': 1 }), ())
  307.             self.sessions[key].setLifetime(self.sessionLifetime)
  308.             if cookie == key:
  309.                 self.sessions[key].sessionJustStarted = True
  310.                 return (urlToChild(ctx, *segments[1:]), ())
  311.             return self.checkLogin(ctx, self.sessions[key], segments[1:], sessionURL = segments[0])
  312.         path.startswith(SESSION_KEY)
  313.         return self._delegate(ctx, segments)
  314.  
  315.     
  316.     def _delegate(self, ctx, segments):
  317.         """Identify the session by looking at cookies and HTTP auth headers, use that
  318.         session key to identify the wrapped resource, then return a deferred
  319.         which fires a 2-tuple of (resource, segments) to the top-level
  320.         redirection code code which will delegate IResource's renderHTTP or
  321.         locateChild methods to it
  322.         """
  323.         request = inevow.IRequest(ctx)
  324.         cookie = request.getCookie(self.cookieKey)
  325.         userpass = (request.getUser(), request.getPassword())
  326.         httpAuthSessionKey = 'HTTP AUTH: %s:%s' % userpass
  327.         for sessionKey in (cookie, httpAuthSessionKey):
  328.             if sessionKey in self.sessions:
  329.                 session = self.sessions[sessionKey]
  330.                 return self.checkLogin(ctx, session, segments)
  331.         
  332.         if userpass != ('', ''):
  333.             sz.checkExpired()
  334.             return self.checkLogin(ctx, sz, segments, None, UsernamePassword(*userpass))
  335.         rd = self.createSession(ctx, segments)
  336.         return (rd, ())
  337.  
  338.     
  339.     def createSession(self, ctx, segments):
  340.         '''
  341.         Create a new session for this request, and redirect back to the path
  342.         given by segments.
  343.         '''
  344.         request = inevow.IRequest(ctx)
  345.         newCookie = _sessionCookie()
  346.         if self.useCookies:
  347.             if self.secureCookies and request.isSecure():
  348.                 secure = True
  349.             else:
  350.                 secure = False
  351.             if self.persistentCookies and self.sessionLifetime:
  352.                 expires = http.datetimeToString(time.time() + self.sessionLifetime)
  353.             else:
  354.                 expires = None
  355.             request.addCookie(self.cookieKey, newCookie, path = '/%s' % '/'.join(request.prepath), secure = secure, expires = expires, domain = self.cookieDomainForRequest(request))
  356.         
  357.         sz = self.sessions[newCookie] = self.sessionFactory(self, newCookie)
  358.         sz.args = request.args
  359.         sz.fields = request.fields
  360.         sz.method = request.method
  361.         sz.received_headers = request.received_headers
  362.         sz.checkExpired()
  363.         return urlToChild(ctx, SESSION_KEY + newCookie, *segments)
  364.  
  365.     
  366.     def checkLogin(self, ctx, session, segments, sessionURL = None, httpAuthCredentials = None):
  367.         '''
  368.         Associate the given request with the given session and:
  369.  
  370.             - log the user in to our portal, if they are accessing a login URL
  371.  
  372.             - log the user out from our portal (calling their logout callback),
  373.               if they are logged in and accessing a logout URL
  374.  
  375.             - Move the request parameters saved on the session, if there are
  376.               any, onto the request if a session just started or a login
  377.               just succeeded.
  378.  
  379.         @return:
  380.  
  381.             - if the user is already logged in: a 2-tuple of requestObject,
  382.               C{segments} (i.e. the segments parameter)
  383.  
  384.             - if the user is not logged in and not logging in, call login() to
  385.               initialize an anonymous session, and return a 2-tuple of
  386.               (rootResource, segments-parameter) from that anonymous session.
  387.               This counts as logging in for the purpose of future calls to
  388.               checkLogin.
  389.  
  390.             - if the user is accessing a login URL: a 2-tuple of the logged in
  391.               resource object root and the remainder of the segments (i.e. the
  392.               URL minus __login__) to be passed to that resource.
  393.  
  394.         '''
  395.         request = inevow.IRequest(ctx)
  396.         session.touch()
  397.         request.session = session
  398.         root = url.URL.fromContext(request)
  399.         if sessionURL is not None:
  400.             root = root.child(sessionURL)
  401.         
  402.         request.rememberRootURL(str(root))
  403.         spoof = False
  404.         if getattr(session, 'sessionJustStarted', False):
  405.             del session.sessionJustStarted
  406.             spoof = True
  407.         
  408.         if getattr(session, 'justLoggedIn', False):
  409.             del session.justLoggedIn
  410.             spoof = True
  411.         
  412.         if spoof and hasattr(session, 'args'):
  413.             request.args = session.args
  414.             request.fields = session.fields
  415.             request.content = StringIO.StringIO()
  416.             request.content.close()
  417.             request.method = session.method
  418.             request.received_headers = session.received_headers
  419.             del session.args
  420.             del session.fields
  421.             del session.method
  422.             del session.received_headers
  423.         
  424.         if segments and segments[0] in (LOGIN_AVATAR, LOGOUT_AVATAR):
  425.             authCommand = segments[0]
  426.         else:
  427.             authCommand = None
  428.         if httpAuthCredentials:
  429.             if not not authCommand:
  430.                 raise AssertionError, "HTTP auth support isn't that robust.  Come up with something to do that makes sense here."
  431.             return self.login(request, session, httpAuthCredentials, segments).addErrback(self.authRequiredError, session)
  432.         if authCommand == LOGIN_AVATAR:
  433.             subSegments = segments[1:]
  434.             
  435.             def unmangleURL(.0):
  436.                 (res, segs) = .0
  437.                 session.justLoggedIn = True
  438.                 u = url.URL.fromString(request.getRootURL())
  439.                 for seg in subSegments:
  440.                     u = u.child(seg)
  441.                 
  442.                 return (u, ())
  443.  
  444.             return self.login(request, session, self.getCredentials(request), subSegments).addCallback(unmangleURL).addErrback(self.incorrectLoginError, ctx, subSegments, 'Incorrect login.')
  445.         if authCommand == LOGOUT_AVATAR:
  446.             self.explicitLogout(session)
  447.             return (urlToChild(ctx, *segments[1:]), ())
  448.         r = session.resourceForPortal(self.portal)
  449.         if r:
  450.             return (r[0], segments)
  451.         return self.login(request, session, Anonymous(), segments).addErrback(self.incorrectLoginError, ctx, segments, 'Anonymous access not allowed.')
  452.  
  453.     
  454.     def explicitLogout(self, session):
  455.         """Hook to be overridden if you care about user-requested logout.
  456.  
  457.         Note: there is no return value from this method; it is purely a way to
  458.         provide customized behavior that distinguishes between session-expiry
  459.         logout, which is what 99% of code cares about, and explicit user
  460.         logout, which you may need to be notified of if (for example) your
  461.         application sets other HTTP cookies which refer to server-side state,
  462.         and you want to expire that state in a manual logout but not with an
  463.         automated logout.  (c.f. Quotient's persistent sessions.)
  464.  
  465.         If you want the user to see a customized logout page, just generate a
  466.         logout link that looks like
  467.  
  468.             http://your-site.example.com/__logout__/my/custom/logout/stuff
  469.  
  470.         and the user will see
  471.  
  472.             http://your-site.example.com/my/custom/logout/stuff
  473.  
  474.         as their first URL after becoming anonymous again.
  475.         """
  476.         session.portalLogout(self.portal)
  477.  
  478.     
  479.     def getCredentials(self, request):
  480.         username = request.args.get('username', [
  481.             ''])[0]
  482.         password = request.args.get('password', [
  483.             ''])[0]
  484.         return UsernamePassword(username, password)
  485.  
  486.     
  487.     def login(self, request, session, credentials, segments):
  488.         """
  489.  
  490.         - Calls login() on our portal.
  491.  
  492.         - creates a mind from my mindFactory, with the request and credentials
  493.  
  494.         - Associates the mind with the given session.
  495.  
  496.         - Associates the resource returned from my portal's login() with my
  497.           portal in the given session.
  498.  
  499.         @return: a Deferred which fires a 2-tuple of the resource returned from
  500.         my portal's login() and the passed list of segments upon successful
  501.         login.
  502.  
  503.         """
  504.         mind = self.mindFactory(request, credentials)
  505.         session.mind = mind
  506.         return self.portal.login(credentials, mind, self.credInterface).addCallback(self._cbLoginSuccess, session, segments)
  507.  
  508.     
  509.     def _cbLoginSuccess(self, .1, session, segments):
  510.         (iface, res, logout) = .1
  511.         session.setResourceForPortal(res, self.portal, logout)
  512.         return (res, segments)
  513.  
  514.     
  515.     def incorrectLoginError(self, error, ctx, segments, loginFailure):
  516.         """ Used as an errback upon failed login, returns a 2-tuple of a failure URL
  517.         with the query argument 'login-failure' set to the parameter
  518.         loginFailure, and an empty list of segments, to redirect to that URL.
  519.         The basis for this error URL, i.e. the part before the query string, is
  520.         taken either from the 'referer' header from the given request if one
  521.         exists, or a computed URL that points at the same page that the user is
  522.         currently looking at to attempt login.  Any existing query string will
  523.         be stripped.
  524.         """
  525.         request = inevow.IRequest(ctx)
  526.         error.trap(UnauthorizedLogin)
  527.         referer = request.getHeader('referer')
  528.         if referer is not None:
  529.             u = url.URL.fromString(referer)
  530.         else:
  531.             u = urlToChild(ctx, *segments)
  532.         u = u.clear()
  533.         u = u.add('login-failure', loginFailure)
  534.         return (u, ())
  535.  
  536.     
  537.     def authRequiredError(self, error, session):
  538.         session.expire()
  539.         error.trap(UnauthorizedLogin)
  540.         return (Forbidden(), ())
  541.  
  542.     
  543.     def cookieDomainForRequest(self, request):
  544.         '''
  545.         Specify the domain restriction on the session cookie.
  546.  
  547.         @param request: The request object in response to which a cookie is
  548.             being set.
  549.  
  550.         @return: C{None} or a C{str} giving the domain restriction to set on
  551.             the cookie.
  552.         '''
  553.         pass
  554.  
  555.  
  556.